#include "sys.h"
#include "net.h"
#include "log.h"
#include "heart.h"


void init_heartinfo(HeartInfo_t* hi, char* probeId, int cycle, char *host, uint16_t port)
{
    memset(hi, 0, sizeof(HeartInfo_t));
    hi->probeId = atoi(probeId);
    hi->cycle = cycle;
    strcpy(hi->host, host);
    hi->port = port;
    hi->seq = 1;
}

static uint32_t heart_bit_checkcode(heart_pkt_t* hp)
{
    uint32_t crc_flag = (('W'<<24)|('D'<<16)|('K'<<8)|'J');
    return hp->size ^ hp->seq ^ hp->time ^ hp->probeId ^ crc_flag;
}
static void gen_heart_pkt(heart_pkt_t* hp, HeartInfo_t* hi, uint32_t cmd_id)
{
    hp->size = htonl(sizeof(heart_pkt_t));
    hp->seq = htonl(hi->seq++);
    hp->time = htonl(time(NULL));
    hp->probeId = htonl(hi->probeId);
    hp->cycle = htonl(hi->cycle);
    hp->cmd_id = htonl(cmd_id);

    /* 可以不做校验和，在UDP头里已经对UDP数据部分进行了校验，
    此处做校验为兼容平台之前版本 */
    hp->checkCode = heart_bit_checkcode(hp);
}

void heart_notify(uint32_t cmd_id){
    int fd = 0;
    struct sockaddr_in addr;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0){
        log_syserr("Heart lo socket create error.");
        return;
    }
    sockaddr_set(&addr, AF_INET, htons(HEART_LISTNE_PORT), "127.0.0.1");

    if(0 > connect(fd, (struct sockaddr*)&addr, sizeof(addr))){
        log_syserr("Heart lo socket connect error.");
        close(fd);
        return;
    }

    if(0 > write(fd, &cmd_id, sizeof(cmd_id)))
    {
        log_syserr("heart notify write failed");
    }
    close(fd);
}

void* heart_bit_loop(void* arg)
{

    HeartInfo_t* hi = (HeartInfo_t*)arg;
    heart_pkt_t hp_out, hp_in;
    struct sockaddr_in addr;
    int ret = 0;
    fd_set rdfds;
    struct timeval tmo = {0};
    uint32_t cmd_id = 0, cycle = 0;

    log_info("Heart Loop Run... (pid:%u)", getpid());
retry:
    hi->fd = socket_dgram_new();
    if (hi->fd < 0) {
        log_syserr("Heart create fd error.");
        sleep(1);
        goto retry;
    }
    sockaddr_set(&addr, AF_INET, htons(hi->port), hi->host);

    while (1) {
        FD_ZERO(&rdfds);
        FD_SET(hi->fd, &rdfds);
        tmo.tv_sec = hi->cycle;
        tmo.tv_usec = 0;
        memset(&hp_out, 0, sizeof(heart_pkt_t));
        memset(&hp_in, 0, sizeof(heart_pkt_t));
        ret = select(hi->fd + 1, &rdfds, NULL, NULL, &tmo);
        if (ret < 0) {
            log_syserr("Heart select error.");
            close(hi->fd);
            goto retry;
        } else if (ret == 0) {
            gen_heart_pkt(&hp_out, hi, 0);
            // log_buf(LOG_TRACE, &hp_out, sizeof(heart_pkt_t));
            // log_trace("send heart.");
            ret = sendto(hi->fd, &hp_out, sizeof(heart_pkt_t), 0, (struct sockaddr *)&addr, sizeof(addr));
            if (ret < 0) {
                log_syserr("Heart send failed.");
                close(hi->fd);
                goto retry;
            }
        }

        if (FD_ISSET(hi->fd, &rdfds)) {
            ret = recvfrom(hi->fd, &hp_in, sizeof(heart_pkt_t), 0, NULL, NULL);
            if (ret < 0) {
                log_syserr("Heart read failed");
                close(hi->fd);
                goto retry;
            }
            // log_buf(LOG_TRACE, &hp_in, sizeof(heart_pkt_t));
            if(ret > 0){
                cmd_id = ntohl(hp_in.cmd_id);
                cycle = ntohl(hp_in.cycle);
                if(cmd_id > 0){
                    log_trace("Heart Rcv cmd id:%d", cmd_id);
                    sendto(hi->fd, &hp_in, sizeof(heart_pkt_t), 0, (struct sockaddr *)&addr, sizeof(addr));
                    heart_notify(cmd_id);
                }else{
                    hi->cycle = cycle ? cycle : 5;
                    log_info("Heart cycle %d sec", hi->cycle);
                }
            }
        }
    }
}
